///// FoobarCityStreets /////
    using System;
    using System.Collections.Generic;
    using System.Collections;
    //using System.Linq;
    using System.Text;
    using System.IO;
    using System.Xml;
    using iTextSharp.text;
    using iTextSharp.text.pdf;


namespace FoobarCityStreets
{
    class Program
    {
        // The PdfContentByte we are going to draw to
        protected static PdfTemplate template = null;

        // The PdfWriter that is writing the PDF
        protected static PdfWriter writer;

        // The StringBuffer that holds the characters
        protected static StringBuilder buf = new StringBuilder();

        // Attribute points 
        protected static string points;

        // Attribute fill 
        protected static string fill;

        // Attribute stroke 
        protected static string stroke;

        // Attribute strokeWidth 
        protected static string strokeWidth;

        // Attribute d
        protected static string d;

        // Attribute svgWidth
        protected static string svgWidth;

        // Attribute svgHeight
        protected static string svgHeight;

        // Attribute viewBox
        protected static string viewBox = string.Empty;

        // PageSize and Viewbox parameters
        protected static float[] coordinates = new float[6];

        // Current font-size 
        protected static int fontsize;

        // Current id
        protected static string id;

        // Current link
        protected static string link;

        // Hashtable with the street names
        protected static Hashtable map = new Hashtable();

        // Inner class that holds a text, its starting coordinate and an angle
        protected class Street
        {
            // the street name
            public string name;

            // the x coordinate 
            public float x;

            // the y coordinate
            public float y;

            // the angle 
            public float alpha;

            // the fontsize 
            public int fontsize;

            // @param dx
            // @param dy
            public void calcAlfa(float dx, float dy)
            {
                alpha = (float)(Math.Atan((double)((dy - y) / (dx - x))) * 180 / Math.PI);
            }
        }

        private static void drawPolyline()
        {
            template.SaveState();
            setFill();
            setStroke();
            computePoints();
            template.Stroke();
            template.RestoreState();
        }

        private static void drawPath()
        {
            template.SaveState();
            setFill();
            setStroke();
            computeData();
            template.Stroke();
            template.RestoreState();
        }

        // Sets the fill color
        private static void setFill()
        {
            switch (fill.ToUpper())
            {
                case "AQUAMARINE":
                    template.SetColorFill(BaseColor.MAGENTA);
                    break;
                case "GRAY":
                    template.SetColorFill(BaseColor.GRAY);
                    break;
                case "LAWNGREEN":
                case "GREEN":
                    template.SetColorFill(BaseColor.GREEN);
                    break;
                case "LIGHTYELLOW":
                case "YELLOW":
                    //template.SetColorFill(BaseColor.YELLOW);
                    template.SetColorFill(BaseColor.LIGHT_GRAY);
                    break;
                default:
                    break;
            }
        }

        // Sets the stroke color
        private static void setStroke()
        {
            switch (stroke.ToUpper())
            {
                case "BLACK":
                    template.SetColorStroke(BaseColor.BLACK);
                    break;
                case "BLUE":
                    template.SetColorStroke(BaseColor.BLUE);
                    break;
                case "ORANGE":
                    template.SetColorStroke(BaseColor.ORANGE);
                    break;
                case "RED":
                    template.SetColorStroke(BaseColor.RED);
                    break;
                case "YELLOW":
                    template.SetColorStroke(BaseColor.YELLOW);
                    break;
                default:
                    template.SetColorStroke(BaseColor.BLACK);
                    break;
            }

            float w = getPixels(strokeWidth);
            template.SetLineWidth(w);
        }

        // Computes the data of Attribute d
        private static void computeData()
        {
            float x;
            float y;
            string[] strData = d.Split(' ');

            for (int i = 0; i < strData.Length; i++)
            {
                if (i % 3 == 0)
                {
                    x = Convert.ToSingle(strData[i + 1]);
                    y = Convert.ToSingle(strData[i + 2]);

                    switch (strData[i])
                    {
                        case "M":
                            template.MoveTo(x, coordinates[5] - y);
                            break;
                        case "m":
                            break;
                        case "L":
                            template.LineTo(x, coordinates[5] - y);
                            break;
                        case "l":
                            break;
                        case "z":
                        case "Z":
                            template.ClosePathFillStroke();
                            break;
                    }
                }
            }
        }

        // Computes the data of Attribute points
        private static void computePoints()
        {
            float x;
            float y;
            int posComma;
            bool firstTime = true;

            string[] tmpPoints = points.Split(' ');
            foreach (string coords in tmpPoints)
            {
                posComma = coords.IndexOf(',');
                x = Convert.ToInt32(coords.Substring(0, posComma));
                y = Convert.ToInt32(coords.Substring((posComma + 1), (coords.Length - posComma - 1)));
                if (firstTime)
                {
                    template.MoveTo(x, coordinates[5] - y);
                    firstTime = false;
                }
                else
                {
                    template.LineTo(x, coordinates[5] - y);
                    template.MoveTo(x, coordinates[5] - y);
                }
            }
        }

        private static void calcSize()
        {
            coordinates[0] = Convert.ToSingle(getPixels(svgWidth));
            coordinates[1] = Convert.ToSingle(getPixels(svgHeight));
            coordinates[2] = 0;
            coordinates[3] = 0;
            coordinates[4] = coordinates[0];
            coordinates[5] = coordinates[1];
            if (!string.IsNullOrEmpty(viewBox))
            {
                string[] strValues = viewBox.Split(' ');

                for (int i = 0; i < strValues.Length; i++)
                {
                    coordinates[i + 2] = Convert.ToSingle(strValues[i]);
                }
            }
            PdfContentByte content = writer.DirectContent;
            template = content.CreateTemplate(coordinates[4], coordinates[5]);
        }

        // Parses a string that represents a length.
        // 
        // @param length
        //              the string
        // @return the length defined by the String in pixels.
        private static float getPixels(string strPixels)
        {
            float total;
            string chrsLastTwo = string.Empty;

            if (strPixels.Length > 2)
                chrsLastTwo = strPixels.Substring(strPixels.Length - 2, 2);

            switch (chrsLastTwo)
            {
                case "pt":
                    strPixels = strPixels.Replace("pt", "");
                    total = Convert.ToSingle(strPixels);
                    total /= 1.25f;
                    break;
                case "pc":
                    strPixels = strPixels.Replace("pc", "");
                    total = Convert.ToSingle(strPixels);
                    total /= 15f;
                    break;
                case "mm":
                    strPixels = strPixels.Replace("mm", "");
                    total = Convert.ToSingle(strPixels);
                    total /= 3.543307f;
                    break;
                case "cm":
                    strPixels = strPixels.Replace("cm", "");
                    total = Convert.ToSingle(strPixels);
                    total /= 35.443307f;
                    break;
                case "in":
                    strPixels = strPixels.Replace("in", "");
                    total = Convert.ToSingle(strPixels);
                    total /= 90f;
                    break;
                default:
                    total = Convert.ToSingle(strPixels);
                    break;
            }

            return total;
        }

        // Returns the PdfTemplate 
        public static Image getImage()
        {
            return Image.GetInstance(template);
        }

        // Returns the PdfTemplate.
        public static PdfTemplate getTemplate()
        {
            return template;
        }

        // Returns a map with Streets.
        // 
        // @return a map with Streets and their coordinates.
        public static Hashtable getStreets()
        {
            return map;
        }

        // Draws a path
        private static void registerPath()
        {
            float x;
            float y;
            string strID = id;
            string[] strData = d.Split(' ');
            Street street = new Street();

            for (int i = 0; i < strData.Length; i++)
            {
                if (i % 3 == 0)
                {
                    x = Convert.ToSingle(strData[i + 1]);
                    y = Convert.ToSingle(strData[i + 2]);

                    switch (strData[i])
                    {
                        case "M":
                            street.x = x;
                            street.y = coordinates[5] - y;
                            break;
                        case "L":
                            street.calcAlfa(x, coordinates[5] - y);
                            break;
                    }
                }
            }

            map.Add("#" + strID, street);
        }

        static void Main(string[] args)
        {
            // Generates a PDF showing the Map of a City.
            // 
            // @param args:  no arguments needed here

            Console.WriteLine("Chapter 10: City of Foobar");
            Console.WriteLine("-> Creates a Map based on an SVG image.");
            Console.WriteLine("   and an SVG with street names.");
            Console.WriteLine("-> external resource needed:");
            Console.WriteLine("   foobarcity.svg (chapter 10)");
            Console.WriteLine("   and streets.svg");
            Console.WriteLine("-> file generated: foobar_streets.pdf");

            try
            {
                Document.Compress = false;
                Document document = new Document(PageSize.A4);
                writer = PdfWriter.GetInstance(document, new FileStream("foobarcity.pdf", FileMode.Create));
                document.Open();

                // Create an instance of XmlTextReader and call Read method to read the file
                //XmlTextReader textReader = new XmlTextReader(@"C:\Sample Code\iText\PDFs\foobarcity.svg");
                XmlTextReader textReader = new XmlTextReader(@"C:\Sample Code\iText\PDFs\foobarcity.xml");

                while (textReader.Read())
                {
                    if (textReader.NodeType == XmlNodeType.Element)
                    {
                        switch (textReader.Name)
                        {
                            case "polyline":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "points":
                                                points = textReader.Value;
                                                break;
                                            case "fill":
                                                fill = textReader.Value;
                                                break;
                                            case "stroke":
                                                stroke = textReader.Value;
                                                break;
                                            case "stroke-width":
                                                strokeWidth = textReader.Value;
                                                break;
                                        }
                                    }
                                    drawPolyline();
                                }
                                break;
                            case "path":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "d":
                                                d = textReader.Value;
                                                break;
                                            case "fill":
                                                fill = textReader.Value;
                                                break;
                                            case "stroke":
                                                stroke = textReader.Value;
                                                break;
                                            case "stroke-width":
                                                strokeWidth = textReader.Value;
                                                break;
                                        }
                                    }
                                    drawPath();
                                }
                                break;
                            case "svg":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "width":
                                                svgWidth = textReader.Value;
                                                break;
                                            case "height":
                                                svgHeight = textReader.Value;
                                                break;
                                            case "viewBox":
                                                viewBox = textReader.Value;
                                                break;
                                        }
                                    }
                                    calcSize();
                                }
                                break;
                        }
                    }
                    else
                        if (textReader.NodeType == XmlNodeType.EndElement)
                        {
                            buf = new StringBuilder();
                        }
                        else
                            if (textReader.NodeType == XmlNodeType.Text)
                            {
                                buf.Append(textReader.Value.ToString());
                            }
                }

                Image image = getImage();
                image.ScaleToFit(PageSize.A4.Width, PageSize.A4.Height);
                image.SetAbsolutePosition(0, PageSize.A4.Height - image.ScaledHeight);
                document.Add(image);

                //XmlTextReader textReader = new XmlTextReader(@"C:\Sample Code\iText\PDFs\streets.svg");
                textReader = new XmlTextReader(@"C:\Sample Code\iText\PDFs\streets.xml");

                while (textReader.Read())
                {
                    if (textReader.NodeType == XmlNodeType.Element)
                    {
                        switch (textReader.Name)
                        {
                            case "text":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "font-size":
                                                fontsize = Convert.ToInt32(textReader.Value);
                                                break;
                                        }
                                    }
                                }
                                break;
                            case "textPath":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "xlink":
                                                link = textReader.Value;
                                                break;
                                        }
                                    }
                                }
                                break;
                            case "path":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "id":
                                                id = textReader.Value;
                                                break;
                                            case "d":
                                                d = textReader.Value;
                                                break;
                                        }
                                    }
                                    registerPath();
                                }
                                break;
                            case "svg":
                                if (textReader.HasAttributes)
                                {
                                    while (textReader.MoveToNextAttribute())
                                    {
                                        switch (textReader.Name)
                                        {
                                            case "width":
                                                svgWidth = textReader.Value;
                                                break;
                                            case "height":
                                                svgHeight = textReader.Value;
                                                break;
                                            case "viewBox":
                                                viewBox = textReader.Value;
                                                break;
                                        }
                                    }
                                    calcSize();
                                }
                                break;
                        }
                    }
                    else
                        if (textReader.NodeType == XmlNodeType.EndElement)
                        {
                            if (textReader.Name.Equals("textPath"))
                            {
                                Street street = (Street)map[link];
                                street.name = buf.ToString();
                                street.fontsize = fontsize;
                            }

                            buf = new StringBuilder();
                        }
                        else
                            if (textReader.NodeType == XmlNodeType.Text)
                            {
                                buf.Append(textReader.Value.ToString());
                            }
                }

                Hashtable streets = getStreets();
                Street st = new Street();
                BaseFont bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);

                template.BeginText();

                foreach (DictionaryEntry de in streets)
                {
                    st = (Street)de.Value;
                    template.SetFontAndSize(bf, st.fontsize);
                    template.ShowTextAligned(PdfTemplate.ALIGN_LEFT, st.name, st.x, st.y, st.alpha);
                }

                template.EndText();

                image = Image.GetInstance(template);
                image.ScaleToFit(PageSize.A4.Width, PageSize.A4.Height);
                image.SetAbsolutePosition(0, PageSize.A4.Height - image.ScaledHeight);
                document.Add(image);
                document.Close();
            }

            catch (Exception de)
            {
                Console.Error.WriteLine(de.Message + "   " + de.StackTrace);
            }

            Console.ReadLine();
        }
    }
}
